通俗易懂地理解React Fiber
日期:2022-07-01 17:39:12
更新:2022-07-01 17:40:19
标签:Javascript, React
分类:Javascript
Fiber是在React16+首次引入的,至今已经过了好几年了,那么你有了解过Fiber吗?在了解Fiber是什么之前,首先了解一下在Fiber出现之前,React更新视图的原理...

Fiber是在 React16+首次引入的,至今已经过了好几年了,那么你有了解过Fiber吗?
Fiber 是什么
在了解Fiber是什么之前,首先了解一下在 Fiber 出现之前,React 更新视图的原理:通过 setState 改变数据从而触发虚拟 DOM 去进行对比,对比结束后将再进行 DOM 更新。那么更新就会分成两部分:
- 数据更新,触发虚拟 DOM 比较
- 比较完成后更新真实 DOM 节点

当对比少的节点时使用这种方法时比较合理的,但是当我们一次更新有几百个甚至更多组件需要进行对比时,由于 Diff 是一个同步的方法,在进行对比时,由于 JS 单线程的原因,导致其他的事件都无法响应。
当在这个 Diff 的过程中如果有有通过 Input 输入信息,那么他将得不到响应,显然这样体验会非常不好。
针对这样的问题,React 16+中首次引入了React Fiber,就是用于解决渲染复杂组件时产生的卡顿问题。
Fiber 是如何解决问题的
Fiber 的作用就是将 Diff 的工作分割成很多个块,并分散到不同帧中执行。同时也新增了可控制新更新和模块执行优先级的能力。
帧:浏览器帧是根据显示器的刷新率而决定的,比如刷新率为 120Hz,则为 120 帧/s 每帧的时间就为 1000/120 = 8.3ms
简单来说,Fiber 的作用就是对 Diff 的过程进行拆解,把复杂的 Diff 拆分成不同的执行块,再分布到不同的帧中执行。
Fiber 执行过程
Fiber 的结构
React 在创建虚拟 DOM 树的同时也会创建与 DOM 树相同的一个 Fiber 树(Fiber Tree),Fiber Tree是由Fiber Node组成。 每个 Fiber Node 主要由三部分组成: return,children,sibling
-
return:指向父节点
-
children:指向子节点
-
sibling:指向兄弟节点

Fiber Node有上面的三个指针,可以确保 Diff 在暂停之后可以从最后遍历的地方继续进行 Diff。
Fiber 执行顺序
Fiber Node关于执行优先级相关的有两个参数:ExpirationTime和childExpirationTime
ExpirationTime:到期时间,用于判断该节点是否在下一时间片执行
childExpirationTime:快速确定子树中是否有不在等待的变化
Fiber 通过 Fiber Node 的ExpirationTime决定执行的优先级,不同节点的ExpriationTime也可能不同,当当前时间超过ExpirationTime时,此时的 Fiber Node 需要执行的优先级最高,所以,当过期时间越小,执行的优先级就越高
ExpirationTime 有两种计算的方式:
computeInteractiveExpiration: 交互事件的过期时间
computeAsyncExpiration: 异步更新的过期时间
Fiber 更新
当我们在调用setState时,React 会将更新的信息放到 Fiber 的更新队列中,在等待一定的时间后,再将更新队列的信息合并更新到WorkInProgress Tree中。

WorkInProgress Tree: 在遍历Fiber Node时创建的一颗树,一般情况下结构是与 Fiber Node 相同的,在更新是用于更新组件,更新完毕后会将数据同步到 Fiber Tree 中。所以在更新开始是,Fiber Tree 与 WorkInProgress Tree 是结构是相同的。
调度
上文提到,Fiber 是在浏览器空闲的时候进行 Diff 的,而判断浏览器是否空闲则是用浏览器提供的 API 来实现的,用到的 API 包括:requestIdleCallback 和 requestAnimationFrame
- requestIdleCallback: 当浏览器空闲时执行回调函数
- requestAnimationFrame: 浏览器在下次重绘之前调用指定的回调函数
当 workInProgress Tree 生成完之后,会将整颗树交给浏览器渲染到页面上。在这个渲染流程中又分为两步:调和阶段和提交阶段
- 调和阶段:指的是
workInProgress Tree的生成阶段,这个时候的执行是可以被暂停和从暂停的地方重新开始的。
- 提交阶段:指树已经对比并更新完,交给浏览器去 DOM 生成的阶段,此阶段是不可中断的。
调度的过程如下:

workInProgressTree的生成过程

在介绍ExpirationTime也提到过,通过 ExpirationTime 来判断优先级,递归生成 workInProgressTree。
总结
-
Fiber 是 React 为了解决组件嵌套是 Diff 时间过长导致造成卡顿而实现的一种数据结构,通过 Fiber 将 Diff 分块并分散到不同的时间片执行,从而解决 Diff 卡顿问题。
-
Fiber 通过记录父节点,子节点和兄弟节点来保证中断可恢复的。Fiber Node 的执行优先级是通过ExpirationTime决定
-
Fiber 调度过程分成调和阶段和提交阶段,调和阶段可中断,而提交阶段则不能。
参考
- 「React Fiber」详细解析
- React
- React 源码解析
- 阿里三面:灵魂拷问——有 react fiber,为什么不需要 vue fiber 呢?
- React 运行机制